package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.vo.SignResultVO;

import com.tianji.learning.mq.message.SignMessage;
import com.tianji.learning.service.SignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

import static com.tianji.common.constants.MqConstants.Exchange.LEARNING_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.SIGN_IN;
import static com.tianji.common.utils.DateUtils.DATE_SUFFIX_FORMATTER;
import static com.tianji.learning.constants.RedisConstans.SGIN_RECORD_KEY_PREFIX;


/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author 东哥
 * @since 2024-06-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements SignRecordService {
  private final StringRedisTemplate redisTemplate;
  private final RabbitMqHelper mqHelper;

    @Override
    public SignResultVO addSignRecord() {
      //签到
        //1.1 获取当前登录用户
        Long userId = UserContext.getUser();

        //1.2 获取当前日期
        LocalDate now = LocalDate.now();
        String dateStr = now.format(DATE_SUFFIX_FORMATTER);

        //1.3 拼接key
        String key = SGIN_RECORD_KEY_PREFIX + userId +dateStr;

        //1.4计算offset值
        int offset = now.getDayOfMonth() - 1;

        //1.5 签到并校验是否是重复签到
        Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(exists)){
            throw new BizIllegalException("不允许重复签到！");
        }
        //2. 计算连续签到天数
      int days =  countSignDays(key, now.getDayOfMonth());

        //3/计算签到奖励积分
         int rewardPoints =  computePoints(days);
        // 签到积分是基本得分+奖励积分
        // mqHelper.send(LEARNING_EXCHANGE, SIGN_IN, SignMessage.of(userId,rewardPoints+1));
        // 签到积分是基本得分+奖励积分
        //封装vo
        SignResultVO vo = new SignResultVO();
        vo.setSignDays(days);
        vo.setRewardPoints(rewardPoints);

        //5/ 保存积分明细记录（发送MQ消息）
        try {
            mqHelper.send(LEARNING_EXCHANGE,SIGN_IN, SignMessage.of(userId,vo.totalPoints()));
        } catch (Exception e) {
            log.error("签到发送新增积分消息失败：{}",e);
        }

        return vo;
    }
    /**
     * 计算连续签到奖励积分
     *
     * @param days
     * @return
     */
    private int computePoints(int days) {
        switch (days){
            case 7:
                return 10;
            case 14:
                return 20;
            case 28:
                return 40;
            default:
                return 0;
        }

    }

    private int countSignDays(String key, int dayOfMonth) {

        //1.通过bitfield命令获取本月从第一天到今天为止的所有签到记录
        List<Long> result = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                .valueAt(0));
        if (ObjectUtil.isEmpty(result)){
            return 0;
        }
        int signResult = result.get(0).intValue();

        //定义一个计算器
        int days = 0;

        //3.用签到查询结果不断跟1进行与运算，并且判断是否等于1
        while ((signResult & 1) == 1){
            //连续签到天数 + 1
            days++;
            //签到查询结果无符号右移一位
            signResult = signResult >> 1;
        }
        //4.返回连续签到天数
        return days;

    }
//**

    /**
     * 查询签到记录
     * @return
     */

    @Override
    public Byte[] querySignRecords() {

        //1、获取key
        //1.1获取当前登录用户
        Long userId = UserContext.getUser();

        //1.2 获取当前日期
        LocalDate now = LocalDate.now();
        String dateStr = now.format(DATE_SUFFIX_FORMATTER);

        //1.3 拼接key
        String key = SGIN_RECORD_KEY_PREFIX + userId +dateStr;

        //2、查询截止到当天的签到记录
        int dayOfMonth = now.getDayOfMonth();
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                        .valueAt(0));
        if (ObjectUtil.isEmpty(result)){
            return new Byte[]{};

        }
        int signResult = result.get(0).intValue();

        //3.循环每一天的签到情况，赋值给byte数组
        Byte[] arr = new Byte[dayOfMonth];

        int pos = dayOfMonth - 1 ;
        while (pos>=0){
            arr[pos--] = (byte) (signResult & 1);
            signResult = signResult >> 1;
        }

        return arr;
    }
}
